#ifndef __CORE_COMMON_LOG_H_INCLUDE__
#define __CORE_COMMON_LOG_H_INCLUDE__

#include <core/common/global.h>
#include <core/common/file.h>
#include <core/common/multi-thread.h>
#include <core/common/singleton.h>

// format
#define __ULOG_FMT(MODULE, FORMAT)        MODULE"(%d)::"FORMAT, __LINE__
#define __ULOG_FMT_ERR(MODULE, FORMAT)    MODULE"(%d)::"FORMAT" (%u:%s)", __LINE__
#define __ULOG_FMTX(MODULE, FORMAT)       "%s:%d, "MODULE"::"FORMAT, __FILE__, __LINE__

//#define __ULOG_FMT(MODULE, FORMAT)        MODULE" : "FORMAT"\n"
//#define __ULOG_FMT_ERR(MODULE, FORMAT)    MODULE" : "FORMAT", error(%d), describe(%s)\n"
//#define __ULOG_FMTX(MODULE, FORMAT)       "%s:%d, "MODULE" : "FORMAT, __FILE__, __LINE__

// function
#define __ULOG_TRACE   core::common::Log::instance().trace
#define __ULOG_INFO    core::common::Log::instance().info
#define __ULOG_WARNING core::common::Log::instance().warning
#define __ULOG_ERROR   core::common::Log::instance().error
#define __ULOG_FATAL   core::common::Log::instance().fatal
#define __ULOG_STDERR  core::common::Log::instance().writeErrorConsole
#define __ULOG_DETECT(bTrueExp) if( !bTrueExp ){ core::common::Log::instance().error("ErrorDetect::Operation failed @%s:%d", __FUNCTION__, __LINE__); }
//#define __ULOG_DETECT(bTueExp) bTueExp

#define __ULOG_TRACE_STEP__ core::common::Log::instance().trace("Step::Trace at(%s:%d)", __FUNCTION__, __LINE__)

// general error code
#define __UERR_CODE    core::common::getErrorCode()
#define __UERR_STR     core::common::getErrorDescription().c_str()
#define __UERR_STRV(X) core::common::getErrorDescription(X).c_str()

// network error code
#define __UNET_ERR_CODE    core::common::getNetworkErrorCode()
#define __UNET_ERR_STR     core::common::getNetworkErrorDescription().c_str()
#define __UNET_ERR_STRV(X) core::common::getNetworkErrorDescription(X).c_str()

//
// core::common::Log usage
//
/**
// Startup
core::common::Log::InitInstance();
core::common::Log::Instance().setLevel(core::common::Log::kLogLevelAll);
core::common::Log::Instance().setTimeCapacity(3600 * 24);
core::common::Log::Instance().setSizeCapacity(10 * 1024 * 1024);
core::common::Log::Instance().open(core::common::Log::kLogTypeFile|core::common::Log::kLogTypeStdout, "logname.log", true);

// Write log
__ULOG_ERROR(__ULOG_FMT("ModuleName", "App startup, %s, %d"), szValue, nValue);
__ULOG_ERROR(__ULOG_FMT_ERR("ModuleName", "Some error occur, id(%d)"), id, __UERR_CODE, __UERR_STR);
__ULOG_TRACE(...)
__ULOG_WARNING(...)
__ULOG_INFO(...)
__ULOG_FATAL(...) // log fatal error, exit

// Close
core::common::Log::ExitInstance();
**/

namespace core{ namespace common
{
	typedef void (__STDCALL * LogEventCallback)( uint32 level, const char *fmt, va_list argList );

	class Log;

	class CORE_COMMON_API__ LogFile
	{
	private:
		DISALLOW_COPY_AND_ASSIGN(LogFile);

		uint32 type_;
		FILE  *handle_;
		uint64 size_;
		time_t beginTime_;
		std::string file_;

	public:
		LogFile();
		~LogFile();

		void setType( uint32 type );
		bool open( const std::string &file, bool append );
		bool close();
		int  write( core::common::Log *logger, uint32 level, const std::string &timeValue, const char *fmt, va_list argList );

		inline bool isOpend(){ return NULL != handle_; }
		inline uint32 getType(){ return type_; }
	};

	class CORE_COMMON_API__ Log : public Singleton<Log>
	{
		friend class Singleton<Log>;
		friend class LogFile;

	public:
		typedef enum tagLOG_OUTPUT
		{
			// mode
			kLogTypeNone   = 0x00,
			kLogTypeStdout = 0x01,
			kLogTypeStderr = 0x02,
			kLogTypeFile   = 0x04,
			kLogTypeAll    = 0xff,

			// count
			kLogTypeIdxStdout  = 0,
			kLogTypeIdxStderr  = 1,
			kLogTypeIdxFileOut = 2,
			kLogTypeIdxFileErr = 3,
			kLogTypeCount      = 4
		}LOG_OUTPUT;

		typedef enum tagLOG_LEVEL
		{
			kLogLevelNone    = 0x00,
			kLogLevelTrace   = 0x01,
			kLogLevelInfo    = 0x02,
			kLogLevelWarning = 0x04,
			kLogLevelError   = 0x08,
			kLogLevelFatal   = 0x10,
			kLogLevelAll     = 0xff,
		}LOG_LEVEL;

	protected:
		bool autoAddTime_;
		bool autoAddNewLine_;
		bool milliSecondTime_;
		bool multiOutput_;
		uint32 type_;
		uint32 level_;
		time_t timeCapacity_;
		uint64 sizeCapacity_;

		uint64 size_;
		uint64 traceSize_;
		time_t beginTime_;
		time_t traceBeginTime_;

		core::common::Mutex mutex_;
		LogFile files[kLogTypeCount];

		std::string file_;
		std::string traceFile_;

		// log event
		LogEventCallback eventCallback_;

		Log();
		~Log();

		int  write( uint32 type, uint32 level, const char *fmt, va_list argList );

	public:
		bool open( uint32 type, const std::string &file, bool append, bool multiOutput = true );
		bool close();

		void setEventCallback( core::common::LogEventCallback callback );

		uint64 setSizeCapacity( uint64 size );
		uint32 setTimeCapacity( uint32 timeValue );
		bool   setTimeMilliSeconds( bool milliSeconds );
		uint32 setLevel( uint32 level );
		uint32 getLevel() const;
		uint32 setType( uint32 type );
		uint32 getType() const;
		void setAutoProperty( bool addTime, bool addNewLine );
		bool isAutoAddTime() const;
		bool isAutoAddNewLine() const;
		
		int  trace( const char *fmt, ... ) __FMTCHECK__(printf, 2, 3);
		int  info( const char *fmt, ... ) __FMTCHECK__(printf, 2, 3);
		int  warning( const char *fmt, ... ) __FMTCHECK__(printf, 2, 3);
		int  error( const char *fmt, ... ) __FMTCHECK__(printf, 2, 3);
		int  fatal( const char *fmt, ... ) __FMTCHECK__(printf, 2, 3);

		int  writeErrorConsole( const char *fmt, ... );

		static bool getBackupNameTime( const std::string &backupName, time_t &timeFrom, time_t &timeTo );

		inline static const char *getLevelName( int level )
		{
			switch( level )
			{
			case kLogLevelNone:
				return "NON";
			case kLogLevelTrace:
				return "TRC";
			case kLogLevelInfo:
				return "INF";
			case kLogLevelWarning:
				return "WRN";
			case kLogLevelError:
				return "ERR";
			case kLogLevelFatal:
				return "FTL";
			default:
				return "ALL";
			}
		}
	};
}}

#endif //__ROUTETRACE_TRACELOG_H_INCLUDE__
